#include "stdafx.h"

namespace Melting
{
namespace Parallel
{
	//----------------------------------------------------------------
	//----------------------------------------------------------------
	Thread::Thread( size_t _iStackSize )
    :	mHandle     ( INVALID_THREAD_HANDLE )
    ,   miStackSize ( _iStackSize )
    ,   mbActive    ( false )
    ,   mpFunction  ( nullptr )
    ,	mpData      ( nullptr )
    {
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    Thread::~Thread( void )
    {
		// You'd better wait for them properly...
        if( IsActive() )
        {
            Kill();
        }
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    bool Thread::Kill( void )
    {
        //--------------------------------
        // precondition
        MB_ASSERT( mbActive , "Trying to kill an inactive Thread" );
        //--------------------------------
        mbActive = false;
#ifdef WIN32
        return ( 0 != TerminateThread( mHandle , 1) ); //win32 API
#else   // PTHREAD
        //return ( 0 == pthread_kill( mHandle, 1 ) );
        return ( 0 == pthread_cancel( mHandle ) );
#endif
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    bool Thread::Wait( void )
    {
        //--------------------------------
        // precondition
        MB_ASSERT( mbActive , "Trying to wait for an inactive Thread" );
        //--------------------------------
        mbActive = false;
#ifdef WIN32
        return (WAIT_OBJECT_0 == WaitForSingleObject( mHandle , INFINITE)); //win32 API

#else   // PTHREAD
        return 0 == pthread_join( mHandle, nullptr );

#endif
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    bool Thread::Run( void )
    {
        //--------------------------
        // precondition
        MB_ASSERT( nullptr != mpFunction, "No Function to run in the Thread\n" );
        MB_ASSERT( ! mbActive, "Thread already running ? Or not properly stopped ?\n" );
        //--------------------------
#ifdef WIN32
        INT32 iID;
        mHandle = CreateThread( nullptr
							  , miStackSize
							  ,(LPTHREAD_START_ROUTINE)mpFunction
							  ,(LPVOID) this
							  ,0
							  ,(LPDWORD) & iID );
        mbActive = (NULL != mHandle);
#else   // PTHREAD
        pthread_attr_t attributes;
        pthread_attr_init( & attributes );
        // if no stack size has been specified by the user, then use the default value
        if( 0 == miStackSize )
        {
            pthread_attr_getstacksize ( & attributes, & miStackSize );
        }
        else
        {
            pthread_attr_setstacksize ( &  attributes, miStackSize );
        }
        int iError = pthread_create( &mHandle, & attributes, mpFunction, (void*) this );
        pthread_attr_destroy( & attributes );
        mbActive = ( 0 == iError);
#endif
        return mbActive;
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    bool Thread::Run( ThreadFunctionPtr _pFunction )
    {
        //--------------------------
        // precondition 
        MB_ASSERT( nullptr != _pFunction, "No Function to run in the Thread\n" );
        MB_ASSERT( ! mbActive, "Thread already running ? Or not properly stopped ?\n" );
        //--------------------------
        SetFunction( _pFunction );
        return Thread::Run();
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    bool Thread::Run( ThreadFunctionPtr _pFunction, void* _pData )
    {
        //--------------------------
        // precondition
        MB_ASSERT( nullptr != _pFunction, "No Function to run in the Thread\n" );
        MB_ASSERT( ! mbActive, "Thread already running ? Or not properly stopped ?\n" );
        //--------------------------
        SetData( _pData );
        return Thread::Run( _pFunction );
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    void Thread::SetFunction( ThreadFunctionPtr _pFunction )
    {
        //--------------------------
        // precondition
        //MB_ASSERT( nullptr == mpFunction );
        //--------------------------
        mpFunction = _pFunction;
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    void Thread::SetData( void* _pData )
    {
        //--------------------------
        // precondition
        //MB_ASSERT( nullptr == mpData );
        //--------------------------
        mpData = _pData;
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    void* Thread::GetData( void ) const
    {
        return mpData;
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    bool Thread::IsActive( void ) const
    {
        return mbActive;
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    ThreadHandle Thread::GetHandle( void ) const
    {
        return mHandle;
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    size_t Thread::GetStackSize( void ) const
    {
//        if( 0 != miStackSize )
//        {
//            return miStackSize;
//        }
//        else
//        {
//            // if no stack size has been specified by the user, then use the default value
//            pthread_attr_t attributes;
//            pthread_attr_init( & attributes );
//            pthread_attr_getstacksize ( & attributes, & miStackSize );
//            pthread_attr_destroy( & attributes );
//            return miStackSize;
//        }
        return miStackSize;
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
	bool Thread::SetAffinity( UINT32 _iMask )
	{
        //--------------------------
        // precondition
		MB_ASSERT( mbActive, "Cannot call SetAffinity on a Thread that hasn't Run().\n" ) ;
        //--------------------------

#ifdef WIN32

		return 0 != SetThreadAffinityMask( mHandle, _iMask );

#else   // PTHREAD 
		cpu_set_t cpuset;
		
		CPU_ZERO(&cpuset);

		// max for 32 cpu logical core !
		for( int i = 0; i < 32; i++ )
		{
			int iCoreBit = (1 << i):

			if( (_iMask & iCoreBit) > 0 )
			{
	            CPU_SET( iCoreBit, &cpuset );
			}
		}

		return 0 == pthread_setaffinity_np( mHandle, sizeof(cpu_set_t), &cpuset );

#endif
	}
}
}// namespace Melting